package x2rel.qMap.core;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import x2rel.qMap.logger.Logging;

public class AlgebraXqHandler extends XmlHandler {

    private ArrayList<Variables> temp_vars;
    private ArrayList<Variables> finalList;

    public AlgebraXqHandler(File algXqFile) {

        temp_vars = new ArrayList<Variables>();

        try {

            Logging.getInstance().log("Processamento do documento de mapeamento da ágebra xquery iniciado. (" + algXqFile.getName() + ")");

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = (Document) db.parse(algXqFile);

            Element root = doc.getDocumentElement();

            NodeList elements = root.getChildNodes();

            for (int i = elements.getLength() - 1; i >= 0; i--) {

                this.processOp(elements.item(i));

            }


            for (Variables v : temp_vars) {
                Logging.getInstance().log(v.toString(), 1);
            }

            VariablesHandler vh = new VariablesHandler();
            finalList = vh.processVarList(temp_vars);

            if (finalList != null && !finalList.isEmpty()) {
                for (Variables v : finalList) {
                    //getForValues(v);
                    Logging.getInstance().log("_VARS:\n" + Utils.dump(v));
                    //Logging.getInstance().log(Utils.dump(v), 3);
                }
            }



        } catch (ParserConfigurationException ex) {
            Logging.getInstance().log("O parser não foi configurado corretamente.\nDetalhes: " + ex.getMessage(), 3);
        } catch (SAXException ex) {
            Logging.getInstance().log("Ocorreu um erro ao fazer o parse do arquivo.\nDetalhes: " + ex.getMessage(), 3);
        } catch (IOException ex) {
            Logging.getInstance().log("Ocorreu um erro ler o arquivo.\nDetalhes: " + ex.getMessage(), 3);
        }


    }

    public ArrayList<Variables> getFinalVars() {
        return this.finalList;
    }

    private Variables processOp(Node source) {

        if (!isElementNode(source)) {
            return null;
        }

        String tag = source.getNodeName();

        String attname = ((source.hasAttributes() && source.getAttributes().getNamedItem("name") != null) ? source.getAttributes().getNamedItem("name").getNodeValue() : "");
        //String attname = ((source.hasAttributes()) ? source.getAttributes().getNamedItem("name").getNodeValue() : "");

        String attop = ((source.hasAttributes() && source.getAttributes().getNamedItem("op") != null) ? source.getAttributes().getNamedItem("op").getNodeValue() : "");

        NodeList children = (source.hasChildNodes() ? source.getChildNodes() : null);

        String strlog = "<" + tag;
        strlog += (!attname.isEmpty()) ? " name='" + attname + "'>" : ">";
        Logging.getInstance().log(strlog, 1);


        /*   <input_operator name="Parse">
         *       <input_operator name="Documento.xml" />
         *   </input_operator> 
         */
        if (tag.equalsIgnoreCase("input_operator") && attname.equalsIgnoreCase("Parse")) {

            String docParse;
            for (int i = children.getLength() - 1; i >= 0; i--) {

                if (children.item(i).getNodeType() == Node.ELEMENT_NODE && children.item(i).hasAttributes()) {

                    docParse = children.item(i).getAttributes().getNamedItem("name").getNodeValue();
                    Logging.getInstance().log("Arquivo XML: " + docParse, 1);

                    Variables v = new Variables();
                    v.setName("Parse");
                    v.setValue(docParse);
                    temp_vars.add(v);
                    Logging.getInstance().log("Item adicionado às variáveis temporarias:" + v.toString(), 1);
                    return v;

                }
            }

            /*
             * <??? name="TreeJoin"/>
             */
        } else if (attname.equalsIgnoreCase("TreeJoin")) {
            if (children != null && countChildrenElements(children) == 2) {
                Variables v = new Variables();

                //v.setName("TreeJoin");
                for (int i = 0; i < children.getLength(); i++) {
                    if (children.item(i).getNodeName().equalsIgnoreCase("static_param")) {
                        v.setValue(children.item(i).getTextContent());
                    }
                    if (children.item(i).getNodeName().equalsIgnoreCase("input_operator")) {
                        //v.setReference(children.item(i).getAttributes().getNamedItem("name").getNodeValue());
                        v.setName(children.item(i).getAttributes().getNamedItem("name").getNodeValue());
                    }
                }

                temp_vars.add(v);
                Logging.getInstance().log("Treejoin adicionado às variáveis temporarias:" + v.toString(), 1);

                return v;

            } else {
                Logging.getInstance().log("Ocorreu um erro na leitura do documento de álgebra xquery. (Documento mal estruturado)", 4);
                System.exit(0);
            }



            /*
             * <input_operator name="Join">
             *      <sub_operator>
             *      <input_operator name="Map">
             * </input_operator> 
             */
        } else if (tag.equalsIgnoreCase("input_operator") && attname.equalsIgnoreCase("Join")) {
            if (countChildrenElements(children) == 2) {
                Variables v = new Variables();
                v.setName("Join");
                for (int i = children.getLength() - 1; i >= 0; i--) {
                    if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {

                        if (children.item(i).getNodeName().equalsIgnoreCase("input_operator")) {
                            v.setVarRef(processOp(children.item(i)));
                        } else if (children.item(i).getNodeName().equalsIgnoreCase("sub_operator")) {
                            v.setVarValue(processOp(children.item(i)));
                        }

                    }
                }

                temp_vars.add(v);
                Logging.getInstance().log("Item adicionado às variáveis temporarias:" + v.toString(), 1);

                return v;

            } else {
                Logging.getInstance().log("Ocorreu um erro na leitura do documento de álgebra xquery. (Documento mal estruturado)", 4);
                System.exit(0);
            }




            /*
             * WHERE
             */
        } else if (attop.equalsIgnoreCase("Where")) {

            for (int i = 0; i < children.getLength(); i++) {


                if (children.item(i) != null && children.item(i).getNodeType() == Node.ELEMENT_NODE
                        && children.item(i).getNodeName().equalsIgnoreCase("input_operator")) {


                    Node child = children.item(i);
                    String childattname = ((child.hasAttributes() && child.getAttributes().getNamedItem("name") != null) ? child.getAttributes().getNamedItem("name").getNodeValue() : "");


                    if (childattname.toLowerCase().startsWith("some")) {

                        this.processWhere(children.item(i));

                    }
                    
                    if(childattname.equalsIgnoreCase("and") || childattname.equalsIgnoreCase("or")){
                        
                        NodeList grandsons = children.item(i).getChildNodes();
                        
                        for(int k=0; k < grandsons.getLength(); k++){
                            
                            if(grandsons.item(k).getNodeType()== Node.ELEMENT_NODE && 
                                    grandsons.item(k).getNodeName().equalsIgnoreCase("input_operator")){
                                
                                this.processWhere(grandsons.item(k), childattname);                    
                            }
                        }
                        
                        
                    }
                        

                }
            }



            //} else if (tag.equalsIgnoreCase("input_operator") && attname.toLowerCase().startsWith("some"){






            /*
             * <static_param>?</static_param>
             */
        } else if (tag.equalsIgnoreCase("static_param")) {

            if (children != null) {

                if (!hasChildren(children)) {
                    Variables v = new Variables();
                    v.setName("param");
                    v.setValue(source.getTextContent());
                    Logging.getInstance().log("Parâmetro estático passado!!" + v, 2);
                    return v;
                } else {
                    for (int i = children.getLength() - 1; i >= 0; i--) {
                        if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
                            return processOp(children.item(i));
                            //Logging.getInstance().log("N:"+children.getLength(),1);
                        }
                    }
                }
            }



            /*
             * <input_operator name="Map"> 
             */
        } else if (tag.equalsIgnoreCase("input_operator") && attname.equalsIgnoreCase("Map")
                || tag.equalsIgnoreCase("sub_operator") && attname.equalsIgnoreCase("Map")) {

            try {
                // @op = FOR
                if (children != null && attop.equalsIgnoreCase("For")) {

                    /*
                     * Cláusula For 
                     * 
                     * <input_operator name="Map"> 
                     *      <sub_operator [name="?"]/> 
                     *      <input_operator [name="?"]/> 
                     *      <static_param [name="?"]/> 
                     * </input_operator> 
                     * 
                     * ou
                     * 
                     * <input_operator name="Map"> 
                     *      <sub_operator [name="?"]/> 
                     *      <input_operator [name="?"]/> 
                     * </input_operator> 
                     */

                    Variables v = new Variables();
                    v.setName("For");

                    for (int i = children.getLength() - 1; i >= 0; i--) {
                        if (children.item(i) != null && children.item(i).getNodeType() == Node.ELEMENT_NODE
                                && children.item(i).getNodeName().equalsIgnoreCase("static_param")) {

                            Variables v_let = new Variables();
                            v_let.setName("Let");
                            v_let.setVarValue(processOp(children.item(i)));
                            Logging.getInstance().log("\n\n =========== >> >> Let!!\n" + v_let.toString(), 1);
                            temp_vars.add(v_let); // Let

                        }

                        if (children.item(i) != null && children.item(i).getNodeType() == Node.ELEMENT_NODE
                                && children.item(i).getNodeName().equalsIgnoreCase("input_operator")) {
                            v.setVarValue(processOp(children.item(i)));
                        }

                        if (children.item(i) != null && children.item(i).getNodeType() == Node.ELEMENT_NODE
                                && children.item(i).getNodeName().equalsIgnoreCase("sub_operator")) {
                            v.setVarRef(processOp(children.item(i)));
                        }

                    }
                    Logging.getInstance().log("\n\n =========== >> >> For!!\n" + v.toString(), 1);
                    temp_vars.add(v); // For



                    /*
                     * Map genérico
                     * <input_operator name="Map"> 
                     *      <sub_operator [name="?"]/> 
                     *      <input_operator [name="?"]/> 
                     * </input_operator> 
                     */
                } else if (children != null && countChildrenElements(children) == 2) {

                    Variables v = new Variables();
                    v.setName("Map");

                    for (int i = children.getLength() - 1; i >= 0; i--) {

                        if (children.item(i) != null && children.item(i).getNodeName().equalsIgnoreCase("input_operator")) {
                            v.setVarValue(processOp(children.item(i)));
                        }

                        if (children.item(i) != null && children.item(i).getNodeName().equalsIgnoreCase("sub_operator")) {
                            v.setVarRef(processOp(children.item(i)));
                        }

                    }
                    temp_vars.add(v);
                    Logging.getInstance().log("Item adicionado às variáveis temporarias:" + v.toString(), 1);

                    return v;

                } else {
                    throw new Exception("Erro!");
                }

            } catch (Exception e) {
                Logging.getInstance().log("Ocorreu um erro ao processar o documento de álgebra xquery. " + e.getMessage(), 4);
                System.exit(0);
            }


            /*
             * Operador "Return"
             */
        } else if (attname.equalsIgnoreCase("Element") && (children != null)) {



            for (int i = 0; i < children.getLength(); i++) {
                /*
                if (children.item(i) != null && children.item(i).getNodeType() == Node.ELEMENT_NODE
                && children.item(i).getNodeName().equalsIgnoreCase("static_param")) {
                v.setAlias(children.item(i).getTextContent());
                }*/
                if (children.item(i) != null && children.item(i).getNodeType() == Node.ELEMENT_NODE) {

                    if (children.item(i).getNodeName().equalsIgnoreCase("sub_operator")) {
                        Node child = children.item(i);
                        String childattname = ((child.hasAttributes() && child.getAttributes().getNamedItem("name") != null) ? child.getAttributes().getNamedItem("name").getNodeValue() : "");

                        // Tag aninhadas no return
                        if (childattname.equalsIgnoreCase("Element")) {
                            processOp(children.item(i));
                        }

                        // Valor direto, sem xpath
                        if (childattname.startsWith("#glx")) {
                            Variables v = new Variables();
                            v.setName("Return");
                            v.setValue(childattname);
                            temp_vars.add(v);
                        }

                        // Valor (s) com xpath
                        if (childattname.equalsIgnoreCase("fs:distinct-docorder")) {
                            Variables v = new Variables();
                            v.setName("Return");
                            v.setVarValue(processOp(child));
                            temp_vars.add(v);
                        }

                        //
                        if (childattname.equalsIgnoreCase("attribute")) {
                            Variables v = new Variables();
                            v.setName("Return");
                            for (int j = 0; j < child.getChildNodes().getLength(); j++) {
                                if (child.getChildNodes().item(j) != null && child.getChildNodes().item(j).getNodeType() == Node.ELEMENT_NODE) {
                                    if (child.getChildNodes().item(j).getNodeName().equalsIgnoreCase("sub_operator")) {
                                        v.setVarValue(processOp(child.getChildNodes().item(j)));
                                    }
                                }
                            }

                            temp_vars.add(v);
                        }
                    }

                }
            }


        } else if (tag.equalsIgnoreCase("operator")) {
            if (children != null) {
                for (int i = children.getLength() - 1; i >= 0; i--) {
                    if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        processOp(children.item(i));
                        //Logging.getInstance().log("N:"+children.getLength(),1);
                    }
                }
            }
        } else { /* Segue percorrendo o documento */
            if (children != null) {
                int c = 0;
                for (int i = children.getLength() - 1; i >= 0; i--) {
                    if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        c++;
                        if (countChildrenElements(children) == 1 || countChildrenElements(children) == c || attname.equalsIgnoreCase("fn:data")) {
                            return processOp(children.item(i));
                        } else {
                            processOp(children.item(i));
                        }
                        //Logging.getInstance().log("N:"+children.getLength(),1);
                    }
                }
            }
            //Logging.getInstance().log(strlog, 1);

        }

        return null;

    }
    private void processWhere(Node node) {
        this.processWhere(node, "");
    }
    
    private void processWhere(Node node, String wop) {
        NodeList children = (node.hasChildNodes() ? node.getChildNodes() : null);

        if (children != null) {

            Variables v = new Variables();
            v.setName("Where");
            v.setWhere_op(wop);

            for (int j = children.getLength() - 1; j >= 0; j--) {
                String gsattname = ((children.item(j).hasAttributes() && children.item(j).getAttributes().getNamedItem("name") != null) ? children.item(j).getAttributes().getNamedItem("name").getNodeValue() : "");

                if (children.item(j) != null && children.item(j).getNodeType() == Node.ELEMENT_NODE) {


                    if (gsattname.equalsIgnoreCase("fs:distinct-docorder")) {
                        v.setVarValue(processOp(children.item(j)));
                    }

                    if (gsattname.toLowerCase().startsWith("satisfies")) {
                        v.setValue(gsattname.toLowerCase());

                        for (int k = 0; k < children.item(j).getChildNodes().getLength(); k++) {

                            Node opwhere = children.item(j).getChildNodes().item(k);

                            if (opwhere != null && opwhere.getNodeType() == Node.ELEMENT_NODE
                                    && opwhere.getNodeName().equalsIgnoreCase("sub_operator")) {

                                String wopattname = ((opwhere.hasAttributes()
                                        && opwhere.getAttributes().getNamedItem("name") != null)
                                        ? opwhere.getAttributes().getNamedItem("name").getNodeValue() : "");


                                v.setWhere(wopattname);
                            }
                        }
                    }
                }

            }
            Logging.getInstance().log(Utils.dump(v), 3);
            this.temp_vars.add(v);
        }
    }
}
